﻿using System;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Geometries;
using YJKGridTopologyCal.Geometries.Utilities;

namespace YJKGridTopologyCal.Densify
{
	public class Densifier
	{
		public static IGeometry Densify(IGeometry geom, double distanceTolerance)
		{
			return new Densifier(geom)
			{
				DistanceTolerance = distanceTolerance
			}.GetResultGeometry();
		}

		private static Coordinate[] DensifyPoints(Coordinate[] pts, double distanceTolerance, IPrecisionModel precModel)
		{
			LineSegment lineSegment = new LineSegment();
			CoordinateList coordinateList = new CoordinateList();
			for (int i = 0; i < pts.Length - 1; i++)
			{
				lineSegment.P0 = pts[i];
				lineSegment.P1 = pts[i + 1];
				coordinateList.Add(lineSegment.P0, false);
				double length = lineSegment.Length;
				int num = (int)(length / distanceTolerance) + 1;
				if (num > 1)
				{
					double num2 = length / (double)num;
					for (int j = 1; j < num; j++)
					{
						double segmentLengthFraction = (double)j * num2 / length;
						Coordinate coord = lineSegment.PointAlong(segmentLengthFraction);
						precModel.MakePrecise(coord);
						coordinateList.Add(coord, false);
					}
				}
			}
			coordinateList.Add(pts[pts.Length - 1], false);
			return coordinateList.ToCoordinateArray();
		}

		public Densifier(IGeometry inputGeom)
		{
			this._inputGeom = inputGeom;
		}

		public double DistanceTolerance
		{
			get
			{
				return this._distanceTolerance;
			}
			set
			{
				if (value <= 0.0)
				{
					throw new ArgumentException("Tolerance must be positive");
				}
				this._distanceTolerance = value;
			}
		}

		public IGeometry GetResultGeometry()
		{
			return new Densifier.DensifyTransformer(this._distanceTolerance).Transform(this._inputGeom);
		}

		private readonly IGeometry _inputGeom;

		private double _distanceTolerance;

		private class DensifyTransformer : GeometryTransformer
		{
			public DensifyTransformer(double distanceTolerance)
			{
				this._distanceTolerance = distanceTolerance;
			}

			protected override ICoordinateSequence TransformCoordinates(ICoordinateSequence coords, IGeometry parent)
			{
				Coordinate[] array = Densifier.DensifyPoints(coords.ToCoordinateArray(), this._distanceTolerance, parent.PrecisionModel);
				if (parent is ILineString && array.Length == 1)
				{
					array = new Coordinate[0];
				}
				return this.Factory.CoordinateSequenceFactory.Create(array);
			}

			protected override IGeometry TransformPolygon(IPolygon geom, IGeometry parent)
			{
				IGeometry geometry = base.TransformPolygon(geom, parent);
				if (parent is IMultiPolygon)
				{
					return geometry;
				}
				return Densifier.DensifyTransformer.CreateValidArea(geometry);
			}

			protected override IGeometry TransformMultiPolygon(IMultiPolygon geom, IGeometry parent)
			{
				return Densifier.DensifyTransformer.CreateValidArea(base.TransformMultiPolygon(geom, parent));
			}

			private static IGeometry CreateValidArea(IGeometry roughAreaGeom)
			{
				return roughAreaGeom.Buffer(0.0);
			}

			private readonly double _distanceTolerance;
		}
	}
}
